#include "myhead.h"
#include "server.h"
#include "kernel_list.h"

typedef struct
{
    char ip[16];
    int port;
    char host[22]; // 192.168.116.153:8080
    struct list_head list;
} Friend;

typedef struct
{
    char buf[100];
    char from[22]; // 发送客户端的ip端口 192.168.116.153:8080
    char to[22];   // 目标设备的ip端口 192.168.116.153:8080
} Message;

// 全局socket
int s_fd;

// 全局定义内核链表头结点
Friend *head;
// 创建互斥锁, 线程之间通过同一把锁实现互斥效果
pthread_mutex_t mutex;

Friend *create_node(struct sockaddr_in addr)
{
    Friend *f = malloc(sizeof(Friend));
    strcpy(f->ip, inet_ntoa(addr.sin_addr));
    f->port = ntohs(addr.sin_port);
    sprintf(f->host, "%s:%d", f->ip, f->port);
    INIT_LIST_HEAD(&f->list);
    return f;
}

// 收到新消息查看是否是新人,加到链表队列中
void *get_new_msg(void *arg)
{
    pthread_detach(pthread_self());
    pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
    // Friends是公共资源,在访问公共资源时给他上锁
    pthread_mutex_lock(&mutex);

    struct sockaddr_in *c_addr = arg;
    char ip[16];
    int port;
    char host[22];
    strcpy(ip, inet_ntoa(c_addr->sin_addr));
    port = ntohs(c_addr->sin_port);
    sprintf(host, "%s:%d", ip, port);

    printf("收到 %s 的消息 \n", host);

    // 遍历内核链表判断此次发送消息的设备是否是新人
    Friend *f;
    int flag = 0; // 0 表示当前好友链表没有此人
    list_for_each_entry(f, &head->list, list)
    {
        if (strcmp(f->host, host) == 0)
        {
            flag = 1;
            break;
        }
        printf("%s \n", f->host);
    }

    if (!flag) // 此人是新人,加入链表
    {
        Friend *new_f = create_node(*c_addr);
        list_add(&new_f->list, &head->list);
    }

    // 释放堆空间
    free(c_addr);
    // 解锁
    pthread_mutex_unlock(&mutex);
}

void *broadcast_msg(void *arg)
{
    pthread_detach(pthread_self());
    pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);

    Message *msg = arg;
    // 遍历内核链表判断此次发送消息的设备是否是新人
    Friend *f;

    printf("msg->from: %s \n", msg->from);
    printf("msg->buf: %s \n", msg->buf);
    printf("msg->to: %s \n", msg->to);

    list_for_each_entry(f, &head->list, list)
    {

        if (strcmp(f->host, msg->from) == 0)
        {
            continue;
        }

        struct sockaddr_in s_addr;
        s_addr.sin_family = AF_INET;
        s_addr.sin_addr.s_addr = inet_addr(f->ip);
        s_addr.sin_port = htons(f->port);

        if (strcmp(msg->to, f->host) == 0)
        {
            sendto(s_fd, msg->buf, strlen(msg->buf), 0, (struct sockaddr *)&s_addr, sizeof(s_addr));
            break;
        }
        if (strcmp(msg->to, BROADCAST) == 0)
        {
            sendto(s_fd, msg->buf, strlen(msg->buf), 0, (struct sockaddr *)&s_addr, sizeof(s_addr));
        }
    }
       

    free(msg);
}

int main(int argc, char const *argv[])
{
    // 1. 创建socket
    s_fd = socket(AF_INET, SOCK_DGRAM, 0);

    // 2. 设置socket属性为端口可复用
    int option_val = 1;
    int ret = setsockopt(s_fd, SOL_SOCKET, SO_REUSEADDR, &option_val, sizeof(option_val));

    // 3. 绑定ip以及端口号
    struct sockaddr_in s_addr;
    s_addr.sin_family = AF_INET;
    s_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    s_addr.sin_port = htons(PORT);
    ret = bind(s_fd, (struct sockaddr *)&s_addr, sizeof(s_addr));

    if (ret == -1)
    {
        perror("服务器ip端口绑定失败 \n");
        return -1;
    }

    // 创建内核链表
    head = create_node(s_addr);

    // 初始化互斥锁
    pthread_mutex_init(&mutex, NULL);

    // 开启循环接收客户端使用udp发送的消息
    char buf[100];

    while (1)
    {
        bzero(buf, 100);
        struct sockaddr_in *c_addr = malloc(sizeof(struct sockaddr_in));
        socklen_t len = sizeof(struct sockaddr_in);
        recvfrom(s_fd, buf, 100, 0, (struct sockaddr *)c_addr, &len);

        if (strcmp(buf, CONNECT) == 0)
        {
            pthread_t tid1;
            pthread_create(&tid1, NULL, get_new_msg, c_addr);
        }
        else
        {
            // 生成msg
            Message *msg = malloc(sizeof(Message));

            char ip[16];
            int port;
            strcpy(ip, inet_ntoa(c_addr->sin_addr));
            port = ntohs(c_addr->sin_port);
            sprintf(msg->from, "%s:%d", ip, port);
            // 根据用户用户发送的消息判断你当前是广播还是发给指定的人
            // BROADCAST#消息          广播消息
            // 192.168.6.3:12345#消息  私聊
           
            strcpy(msg->to,  strtok(buf, "#"));
            strcpy(msg->buf, strtok(NULL, "#"));

            printf("msg->from: %s \n", msg->from);
            printf("msg->buf: %s \n", msg->buf);
            printf("msg->to: %s \n", msg->to);
            // 有新的消息,专门创建一个线程判断当前消息发送方是否是新用户
            pthread_t tid2;
            pthread_create(&tid2, NULL, broadcast_msg, msg);
        }
    }

    return 0;
}
